package com.my.db.test.mybatis.enums.config;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 *
 * FastJson 的配置参考文档链接
 *
 * 由于在JsonDeserializer中DeserializationContext无法获取到实际的类信息，这意味着单独使用JsonDeserializer无法作为枚举的通用解决方案，我们必须为每一个枚举类定制一个反序列化处理方案。
 *
 * 要实现通用的解决方案，需要实现ContextualDeserializer辅助获取转换时的类信息（createContextual每个枚举类只会触发一次，之后都使用该方法返回的反序列化器处理）。
 * ————————————————
 * 版权声明：本文为CSDN博主「ice-maple」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
 * 原文链接：https://blog.csdn.net/qq_33306246/article/details/106933613
 *
 * 使用此序列化器备注的字段将能够被正确处理。
 * 如果需要更大范围的采用此序列化器，将所有的枚举类型默认都委托给JacksonEnumConverter处理，可以修改默认的HttpMessageConverter。
 * @Configuration
 * public class JacksonConfig {
 *     @Bean
 *     public HttpMessageConverter<?> httpMessageConverter(ObjectMapper objectMapper) {
 *         SimpleModule simpleModule = new SimpleModule();
 *         simpleModule.addDeserializer(Enum.class, new JacksonEnumConverter());
 *         objectMapper.registerModule(simpleModule);
 *         return new MappingJackson2HttpMessageConverter(objectMapper);
 *     }
 * }
 * 或者在JacksonEnumConverter上使用JsonComponent注解标记。
 * 或者
 @JsonDeserialize(using = JacksonEnumDeserializer.class)
 private CustomEnums.SexEnum sex;
 * @JsonComponent
 * public class JacksonEnumDeserializer extends JsonDeserializer<Enum<?>> implements ContextualDeserializer
 *
 * @author timo 2021/3/25
 */
//@JsonComponent
@Slf4j
@Setter
public class JacksonEnumDeserializer extends JsonDeserializer<Enum<?>> implements ContextualDeserializer {
    private Class<?> clazz;
    // ctx.getContextualType() 获取不到类信息
    @Override
    public Enum<?> deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException, JsonProcessingException {
        Class<?> enumType = clazz;
        if (Objects.isNull(enumType) || !enumType.isEnum()) {
            return null;
        }
        String text = jsonParser.getText();
        Method method = StringToEnumConverterFactory.getMethod(clazz);
        Enum<?>[] enumConstants = (Enum<?>[]) enumType.getEnumConstants();

        // 将值与枚举对象对应并缓存
        for (Enum<?> e : enumConstants) {
            try {
                if (Objects.equals(method.invoke(e).toString(), text)) {
                    return e;
                }
            } catch (IllegalAccessException | InvocationTargetException ex) {
                log.error("获取枚举值错误!!! ", ex);
            }
        }
        return null;
    }

    /**
     * 为不同的枚举获取合适的解析器
     *
     * @param ctx      ctx
     * @param property property
     */
    @Override
    public JsonDeserializer<Enum<?>> createContextual(DeserializationContext ctx, BeanProperty property) throws JsonMappingException {
        Class<?> rawCls = ctx.getContextualType().getRawClass();
        JacksonEnumDeserializer converter = new JacksonEnumDeserializer();
        converter.setClazz(rawCls);
        return converter;
    }
}
